<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>海明码校验演示</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            max-width: 900px;
            margin: 0 auto;
            padding: 20px;
            color: #333;
            background-color: #f5f5f5;
        }
        h1 {
            color: #2c3e50;
            text-align: center;
            margin-bottom: 30px;
        }
        .container {
            background-color: white;
            border-radius: 8px;
            padding: 25px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .input-section {
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        .bit-inputs {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .bit-input {
            width: 40px;
            height: 40px;
            text-align: center;
            font-size: 18px;
            border: 2px solid #ddd;
            border-radius: 4px;
            outline: none;
            transition: border-color 0.3s;
        }
        .bit-input:focus {
            border-color: #3498db;
        }
        .button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        .button:hover {
            background-color: #2980b9;
        }
        .button:disabled {
            background-color: #95a5a6;
            cursor: not-allowed;
        }
        .result-section {
            margin-top: 30px;
        }
        .step {
            margin-bottom: 25px;
            padding: 15px;
            border-left: 4px solid #3498db;
            background-color: #f8f9fa;
        }
        .step-title {
            font-weight: bold;
            margin-bottom: 10px;
            color: #2c3e50;
        }
        .bits-container {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin: 15px 0;
        }
        .bit {
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 4px;
            font-weight: bold;
        }
        .data-bit {
            background-color: #e8f4fd;
            border: 2px solid #3498db;
        }
        .parity-bit {
            background-color: #fef9e7;
            border: 2px solid #f1c40f;
        }
        .error-bit {
            background-color: #fdeaea;
            border: 2px solid #e74c3c;
            animation: pulse 1s infinite;
        }
        .corrected-bit {
            background-color: #eafaf1;
            border: 2px solid #27ae60;
        }
        .position-label {
            font-size: 12px;
            color: #7f8c8d;
            margin-top: 5px;
        }
        .calculation {
            background-color: #f8f9fa;
            padding: 10px;
            border-radius: 4px;
            margin-top: 10px;
            font-family: monospace;
        }
        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.05); }
            100% { transform: scale(1); }
        }
        .explanation {
            margin-top: 10px;
            font-size: 14px;
            color: #7f8c8d;
        }
    </style>
</head>
<body>
    <h1>海明码校验演示</h1>
    
    <div class="container">
        <div class="input-section">
            <h2>输入原始数据</h2>
            <div class="bit-inputs" id="bitInputs">
                <!-- 输入框将通过JS动态生成 -->
            </div>
            <div>
                <button class="button" id="generateBtn">生成海明码</button>
                <button class="button" id="randomBtn">随机数据</button>
                <button class="button" id="resetBtn">重置</button>
            </div>
        </div>
        
        <div class="result-section">
            <div id="stepsContainer">
                <!-- 步骤将通过JS动态生成 -->
            </div>
        </div>
    </div>

    <script>
        // 默认数据位数
        const DEFAULT_DATA_BITS = 4;
        
        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            initializeInputs(DEFAULT_DATA_BITS);
            document.getElementById('generateBtn').addEventListener('click', generateHammingCode);
            document.getElementById('randomBtn').addEventListener('click', generateRandomData);
            document.getElementById('resetBtn').addEventListener('click', reset);
        });
        
        // 初始化输入框
        function initializeInputs(numBits) {
            const container = document.getElementById('bitInputs');
            container.innerHTML = '';
            
            for (let i = 0; i < numBits; i++) {
                const input = document.createElement('input');
                input.type = 'text';
                input.className = 'bit-input';
                input.maxLength = 1;
                input.value = '0';
                input.dataset.index = i;
                
                // 只允许输入0或1
                input.addEventListener('input', function() {
                    if (this.value !== '0' && this.value !== '1') {
                        this.value = '0';
                    }
                });
                
                container.appendChild(input);
            }
        }
        
        // 获取输入数据
        function getInputData() {
            const inputs = document.querySelectorAll('.bit-input');
            const data = [];
            
            inputs.forEach(input => {
                data.push(parseInt(input.value));
            });
            
            return data;
        }
        
        // 生成随机数据
        function generateRandomData() {
            const inputs = document.querySelectorAll('.bit-input');
            
            inputs.forEach(input => {
                input.value = Math.random() > 0.5 ? '1' : '0';
            });
        }
        
        // 重置
        function reset() {
            const inputs = document.querySelectorAll('.bit-input');
            
            inputs.forEach(input => {
                input.value = '0';
            });
            
            document.getElementById('stepsContainer').innerHTML = '';
        }
        
        // 计算需要的校验位数量
        function calculateParityBits(dataBits) {
            let r = 0;
            while (Math.pow(2, r) < dataBits + r + 1) {
                r++;
            }
            return r;
        }
        
        // 生成海明码
        function generateHammingCode() {
            const originalData = getInputData();
            const dataBits = originalData.length;
            const parityBits = calculateParityBits(dataBits);
            const totalBits = dataBits + parityBits;
            
            // 清空步骤容器
            const stepsContainer = document.getElementById('stepsContainer');
            stepsContainer.innerHTML = '';
            
            // 步骤1: 显示原始数据
            addStep(stepsContainer, '步骤1: 原始数据', 
                createBitsHTML(originalData, 'data-bit'), 
                `原始数据包含 ${dataBits} 位: ${originalData.join('')}`);
            
            // 步骤2: 计算校验位数量
            addStep(stepsContainer, '步骤2: 计算校验位数量', 
                `需要 ${parityBits} 个校验位来保护 ${dataBits} 个数据位`, 
                `根据公式 2^r ≥ m + r + 1，其中 m = ${dataBits}，计算得到 r = ${parityBits}`);
            
            // 步骤3: 插入校验位位置
            const hammingArray = insertParityPositions(originalData, parityBits);
            const positions = hammingArray.map((_, i) => i + 1);
            
            addStep(stepsContainer, '步骤3: 插入校验位位置', 
                createHammingBitsHTML(hammingArray, positions, 'data-bit', 'parity-bit'),
                `校验位位于2的幂次方位置: ${positions.filter((_, i) => isPowerOfTwo(i+1)).join(', ')}`);
            
            // 步骤4: 计算校验位值
            const calculatedHamming = calculateParityBitsValues(hammingArray, parityBits);
            
            let parityCalculation = '';
            for (let i = 0; i < parityBits; i++) {
                const parityPos = Math.pow(2, i);
                const bitsToCheck = getBitsForParity(calculatedHamming, parityPos);
                parityCalculation += `P${parityPos} = ${bitsToCheck.join(' ⊕ ')} = ${calculatedHamming[parityPos-1]}<br>`;
            }
            
            addStep(stepsContainer, '步骤4: 计算校验位值', 
                createHammingBitsHTML(calculatedHamming, positions, 'data-bit', 'parity-bit'),
                parityCalculation);
            
            // 步骤5: 模拟传输错误
            const errorPosition = Math.floor(Math.random() * totalBits) + 1;
            const transmittedData = [...calculatedHamming];
            transmittedData[errorPosition-1] = transmittedData[errorPosition-1] === 0 ? 1 : 0;
            
            addStep(stepsContainer, '步骤5: 模拟传输错误', 
                createErrorBitsHTML(transmittedData, positions, errorPosition),
                `在传输过程中，第 ${errorPosition} 位发生了错误`);
            
            // 步骤6: 检测和纠正错误
            const errorBit = detectError(transmittedData, parityBits);
            
            if (errorBit === 0) {
                addStep(stepsContainer, '步骤6: 错误检测', 
                    '没有检测到错误', 
                    '所有校验位都正确，数据完整无错误');
            } else {
                const correctedData = [...transmittedData];
                correctedData[errorBit-1] = correctedData[errorBit-1] === 0 ? 1 : 0;
                
                addStep(stepsContainer, '步骤6: 错误检测与纠正', 
                    createCorrectedBitsHTML(transmittedData, correctedData, positions, errorBit),
                    `检测到错误位置: ${errorBit}<br>纠正后的数据: ${correctedData.join('')}`);
            }
        }
        
        // 判断是否为2的幂
        function isPowerOfTwo(n) {
            return n && (n & (n - 1)) === 0;
        }
        
        // 插入校验位位置
        function insertParityPositions(data, parityBits) {
            const totalBits = data.length + parityBits;
            const result = [];
            let dataIndex = 0;
            
            for (let i = 1; i <= totalBits; i++) {
                if (isPowerOfTwo(i)) {
                    result.push(0); // 校验位初始化为0
                } else {
                    result.push(data[dataIndex++]);
                }
            }
            
            return result;
        }
        
        // 获取某校验位覆盖的所有位
        function getBitsForParity(data, parityPos) {
            const bits = [];
            
            for (let i = 1; i <= data.length; i++) {
                if ((i & parityPos) !== 0 && i !== parityPos) {
                    bits.push(data[i-1]);
                }
            }
            
            return bits;
        }
        
        // 计算校验位的值
        function calculateParityBitsValues(data, parityBits) {
            const result = [...data];
            
            for (let i = 0; i < parityBits; i++) {
                const parityPos = Math.pow(2, i);
                const bitsToCheck = getBitsForParity(data, parityPos);
                
                // 计算奇偶校验 (使用异或)
                let parityValue = 0;
                for (const bit of bitsToCheck) {
                    parityValue ^= bit;
                }
                
                result[parityPos-1] = parityValue;
            }
            
            return result;
        }
        
        // 检测错误
        function detectError(data, parityBits) {
            let errorPosition = 0;
            
            for (let i = 0; i < parityBits; i++) {
                const parityPos = Math.pow(2, i);
                const bitsToCheck = getBitsForParity(data, parityPos);
                
                // 计算奇偶校验 (使用异或)
                let parityValue = 0;
                for (const bit of bitsToCheck) {
                    parityValue ^= bit;
                }
                
                // 如果计算出的校验值与存储的校验值不同，说明有错误
                if (parityValue !== data[parityPos-1]) {
                    errorPosition += parityPos;
                }
            }
            
            return errorPosition;
        }
        
        // 创建位显示HTML
        function createBitsHTML(bits, bitClass) {
            const container = document.createElement('div');
            container.className = 'bits-container';
            
            bits.forEach((bit, index) => {
                const bitElement = document.createElement('div');
                bitElement.className = `bit ${bitClass}`;
                bitElement.textContent = bit;
                
                const positionLabel = document.createElement('div');
                positionLabel.className = 'position-label';
                positionLabel.textContent = index + 1;
                
                const wrapper = document.createElement('div');
                wrapper.appendChild(bitElement);
                wrapper.appendChild(positionLabel);
                
                container.appendChild(wrapper);
            });
            
            return container;
        }
        
        // 创建海明码位显示HTML
        function createHammingBitsHTML(bits, positions, dataClass, parityClass) {
            const container = document.createElement('div');
            container.className = 'bits-container';
            
            bits.forEach((bit, index) => {
                const isParity = isPowerOfTwo(index + 1);
                const bitElement = document.createElement('div');
                bitElement.className = `bit ${isParity ? parityClass : dataClass}`;
                bitElement.textContent = bit;
                
                const positionLabel = document.createElement('div');
                positionLabel.className = 'position-label';
                positionLabel.textContent = positions[index];
                
                const wrapper = document.createElement('div');
                wrapper.appendChild(bitElement);
                wrapper.appendChild(positionLabel);
                
                container.appendChild(wrapper);
            });
            
            return container;
        }
        
        // 创建错误位显示HTML
        function createErrorBitsHTML(bits, positions, errorPosition) {
            const container = document.createElement('div');
            container.className = 'bits-container';
            
            bits.forEach((bit, index) => {
                const isError = index + 1 === errorPosition;
                const isParity = isPowerOfTwo(index + 1);
                
                const bitElement = document.createElement('div');
                bitElement.className = `bit ${isError ? 'error-bit' : (isParity ? 'parity-bit' : 'data-bit')}`;
                bitElement.textContent = bit;
                
                const positionLabel = document.createElement('div');
                positionLabel.className = 'position-label';
                positionLabel.textContent = positions[index];
                
                const wrapper = document.createElement('div');
                wrapper.appendChild(bitElement);
                wrapper.appendChild(positionLabel);
                
                container.appendChild(wrapper);
            });
            
            return container;
        }
        
        // 创建纠正位显示HTML
        function createCorrectedBitsHTML(originalBits, correctedBits, positions, errorPosition) {
            const container = document.createElement('div');
            container.className = 'bits-container';
            
            originalBits.forEach((bit, index) => {
                const isError = index + 1 === errorPosition;
                const isParity = isPowerOfTwo(index + 1);
                
                const bitElement = document.createElement('div');
                bitElement.className = `bit ${isError ? 'corrected-bit' : (isParity ? 'parity-bit' : 'data-bit')}`;
                bitElement.textContent = isError ? `${bit}→${correctedBits[index]}` : bit;
                
                const positionLabel = document.createElement('div');
                positionLabel.className = 'position-label';
                positionLabel.textContent = positions[index];
                
                const wrapper = document.createElement('div');
                wrapper.appendChild(bitElement);
                wrapper.appendChild(positionLabel);
                
                container.appendChild(wrapper);
            });
            
            return container;
        }
        
        // 添加步骤
        function addStep(container, title, content, explanation) {
            const step = document.createElement('div');
            step.className = 'step';
            
            const titleElement = document.createElement('div');
            titleElement.className = 'step-title';
            titleElement.textContent = title;
            step.appendChild(titleElement);
            
            if (typeof content === 'string') {
                step.innerHTML += content;
            } else {
                step.appendChild(content);
            }
            
            if (explanation) {
                const explanationElement = document.createElement('div');
                explanationElement.className = 'explanation';
                explanationElement.innerHTML = explanation;
                step.appendChild(explanationElement);
            }
            
            container.appendChild(step);
        }
    </script>
</body>
</html>